/**
 * MIT License
 *
 * Copyright (c) 2024 小村程序员
 * Email: 397237738@qq.com
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.open.cobertura.core.analyzer;

import org.open.cobertura.data.CoberturaCoverageCounter;
import org.open.cobertura.data.ICoberturaLineCovered;

import java.util.List;

public class CoberturaSourceCoveredImpl extends CoberturaCoveredImpl{
    private CoberturaLineCovered[] lineCovereds;

    /**
     * 行的覆盖率
     */
    protected List<ICoberturaLineCovered> coberturaLineCovered;

    /**
     * 计算每个方法的指令覆盖和分支覆盖
     * @param coberturaCoveredNode
     */
    public void increase(ICoberturaCoveredNode coberturaCoveredNode){
        super.increase(coberturaCoveredNode.getInstructionCounter(),coberturaCoveredNode.getBranchCounter());
        int firstLine=coberturaCoveredNode.getFirstLine();
        if(firstLine!=-1){
            final int lastLine = coberturaCoveredNode.getLastLine();
            ensureLine(firstLine, lastLine);
            for (int i = firstLine; i <= lastLine; i++) {
                CoberturaLineCovered ll=coberturaCoveredNode.getLine(i);
                this.increaseLine(ll.instructionCounter,ll.branchCounter,i);
            }
        }

    }

    /**
     * 计算指令覆盖和分支覆盖
     * @param instructionCount
     * @param branchCount
     * @param lineNumber
     */
    public void increase(CoberturaCoverageCounter instructionCount,CoberturaCoverageCounter branchCount,int lineNumber){
        super.increase(instructionCount,branchCount);
        this.increaseLine(instructionCount,branchCount,lineNumber);
    }

    public void increaseLine(CoberturaCoverageCounter instructionCount, CoberturaCoverageCounter branchCount, final int line){
        ensureLine(line,line);
        final CoberturaLineCovered ll=getLine(line);
        int oldTotal=ll.getInstructionCounter().getTotalCount();
        CoberturaLineCovered lineCovered= ll.increaseLine(instructionCount,branchCount);
        lineCovereds[line-offset]=lineCovered;
        ICoberturaLineCovered iCoberturaLineCovered=new CoberturaLineCoveredImpl(lineCovered.instructionCounter,lineCovered.branchCounter,line,getFirstLine());
        setCoberturaLineCovered(iCoberturaLineCovered);
        if(instructionCount.getTotalCount()>0){
            if(instructionCount.getCoveredCount()==0){
                if(oldTotal==0){
                    super.increaseMissedLine(1);
                }
            }else{
                if(oldTotal==0){
                    super.increaseCoveredLine(1);
                }
            }
        }

    }


    /**
     * 确定行的覆盖数组大小
     * @param first
     * @param last
     */
    private void ensureLine(int first,int last){
        if(first==-1 || last==-1){
            return;
        }
        if(lineCovereds==null){
            offset=first;
            lineCovereds=new CoberturaLineCovered[last-first+1];
        }else{
            int newFirst=Math.min(getFirstLine(),first);
            int newLast=Math.max(getLastLine(),last);
            int newLength=newLast-newFirst+1;
            if(newLength>lineCovereds.length){
                //动态扩增行覆盖率
                final CoberturaLineCovered[] newLines = new CoberturaLineCovered[newLength];
                System.arraycopy(lineCovereds, 0, newLines, offset - newFirst,
                        lineCovereds.length);
                offset = newFirst;
                lineCovereds = newLines;
            }
        }
    }
    @Override
    public int getFirstLine(){
        return offset;
    }

    /**
     * 得到最后一行
     * @return
     */
    @Override
    public int getLastLine(){
        if(lineCovereds==null){
            return -1;
        }else{
            return offset+lineCovereds.length-1;
        }
    }
    @Override
    public CoberturaLineCovered getLine(int row){
        if(lineCovereds==null || row<getFirstLine() || row>getLastLine()){
            return null;
        }
        CoberturaLineCovered line=lineCovereds[row-offset];
        if(line==null){
            line=new CoberturaLineCovered(0,0,0,0);
            return line;
        }else{
            return line;
        }

    }

    /**
     * 设置行覆盖率，访方法将同一行的指令和分支覆盖累加，如果是新的行，则新增一条到集合中。
     * @param coberturaLineCovered
     */
    private void setCoberturaLineCovered(ICoberturaLineCovered coberturaLineCovered){
        for(int i=0;i<this.coberturaLineCovered.size();i++){
            ICoberturaLineCovered clc=this.coberturaLineCovered.get(i);
            if(clc.getLineNumber()==coberturaLineCovered.getLineNumber()){
                clc.getInstructionCounter().coveredCount+=coberturaLineCovered.getInstructionCounter().coveredCount;
                clc.getInstructionCounter().missedCount+=coberturaLineCovered.getInstructionCounter().missedCount;
                clc.getInstructionCounter().totalCount=clc.getInstructionCounter().missedCount+clc.getInstructionCounter().coveredCount;

                clc.getBranchCounter().coveredCount+=coberturaLineCovered.getBranchCounter().coveredCount;
                clc.getBranchCounter().missedCount+=coberturaLineCovered.getBranchCounter().missedCount;
                clc.getBranchCounter().totalCount=clc.getBranchCounter().missedCount+clc.getBranchCounter().coveredCount;
                this.coberturaLineCovered.set(i,clc);
            }
        }
        if(!this.coberturaLineCovered.contains(coberturaLineCovered)){
            this.coberturaLineCovered.add(coberturaLineCovered);
        }
    }
}
